/*
 * Copyright (c) 2024 Black Sesame Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "capicxx-core-tools/FrancaGeneratorExtensions.h"
#include "capicxx-core-tools/CommonapiPropertyAccessor.h"
#include "capicxx-core-tools/FTypeGenerator.h"
#include "fmt/core.h"
#include "model/FFactory.h"
#include "model/FModelManager.h"
#include "utilities/string_utility.h"
#include <list>
#include <vector>
namespace BstCommonAPI
{
std::shared_ptr<CommonapiPropertyAccessor> FrancaGeneratorExtensions::getAccessor(
    const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    auto accessor = m_accessors[_tc];
    if (accessor == nullptr)
    {
        // It may be the base interface for which we dont have an accessor,
        // if it is defined in another fidl file.
        // We should have at least one property accessor, use this one
        if (!m_accessors.empty())
            accessor = m_accessors.begin()->second;
    }

    if (accessor == nullptr)
        std::cerr << "Could not find accessor for " << _tc->getName() << std::endl;

    return accessor;
}

std::string FrancaGeneratorExtensions::getBundleVersion(std::string bundleName)
{
    if (bundleName == "org.genivi.commonapi.core")
        return "3.2.0";
    else if (bundleName == "org.franca.core")
        return "0.12.0.1";
    else
        return "unknown version";
}

std::string FrancaGeneratorExtensions::getCoreVersion()
{
    std::string bundleName = "org.genivi.commonapi.core";
    return getBundleVersion(bundleName);
}

std::string FrancaGeneratorExtensions::getFrancaVersion()
{
    std::string bundleName = "org.franca.core";
    return getBundleVersion(bundleName);
}

std::string FrancaGeneratorExtensions::getCommentedString(std::string str)
{
    return replace_all(str, "\n", "\n* ");
}

std::string FrancaGeneratorExtensions::getLicenseHeader()
{
    return "";
}

std::string FrancaGeneratorExtensions::generateCommonApiLicenseHeader()
{
     std::string header = R"(/*
 * This file was generated by the BST IDL Generator.
 * All manual modifications will be LOST by next generation.
 * It is recommended NOT modify it.
 * Compatible to org.genivi.commonapi.core $CAPI_VER
 * Compatible to org.franca.core $FRANCA_VER
 * Generator Version: fidl-generator $GEN_VER
 */)";

    replace_one(header, "$CAPI_VER", getCoreVersion());
    replace_one(header, "$FRANCA_VER", getFrancaVersion());
    replace_one(header, "$GEN_VER", COMMIT_HASH);
    return header;
}

std::string FrancaGeneratorExtensions::getElementName(const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    if ((fModelElement->getName().empty() || fModelElement->getName() == "") &&
        std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement) != nullptr)
    {
        return "__Anonymous__";
    }
    else
    {
        return fModelElement->getName();
    }
}

std::string FrancaGeneratorExtensions::getVersionPathPrefix(const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    std::string prefix = "";
    auto itsVersion = _tc->getVersion();
    if (itsVersion != nullptr && (itsVersion->getMajor() != 0 || itsVersion->getMinor() != 0))
    {
        prefix = "v" + std::to_string(itsVersion->getMajor()) + "/";
    }
    return prefix;
}

std::string FrancaGeneratorExtensions::getDirectoryPath(const std::shared_ptr<BstIdl::FModel> &fModel)
{
    auto name = fModel->getName();
    while (name.find(".") != name.npos)
        name = name.replace(name.find("."), 1, "/");
    return name;
}

std::string FrancaGeneratorExtensions::getHeaderFile(const std::shared_ptr<BstIdl::FTypeCollection> &fTypeCollection)
{
    return getElementName(fTypeCollection) + ".hpp";
}

std::string FrancaGeneratorExtensions::getHeaderPath(const std::shared_ptr<BstIdl::FTypeCollection> &fTypeCollection)
{
    if (!fTypeCollection)
        return "";
    BstIdl::FModelManager &mgr = BstIdl::FModelManager::getInstance();
    return getVersionPathPrefix(fTypeCollection) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fTypeCollection->getContainer()))) + "/" +
           getHeaderFile(fTypeCollection);
}

std::string FrancaGeneratorExtensions::getSourceFile(const std::shared_ptr<BstIdl::FTypeCollection> &fTypeCollection)
{
    return getElementName(fTypeCollection) + ".cpp";
}

std::string FrancaGeneratorExtensions::getSourcePath(const std::shared_ptr<BstIdl::FTypeCollection> &fTypeCollection)
{
    return getVersionPathPrefix(fTypeCollection) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fTypeCollection->getContainer()))) + "/" +
           getSourceFile(fTypeCollection);
}

std::string FrancaGeneratorExtensions::getProxyBaseHeaderFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "ProxyBase.hpp";
}

std::string FrancaGeneratorExtensions::getProxyBaseHeaderPath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getProxyBaseHeaderFile(fInterface);
}

std::string FrancaGeneratorExtensions::getProxyHeaderFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "Proxy.hpp";
}

std::string FrancaGeneratorExtensions::getProxyHeaderPath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getProxyHeaderFile(fInterface);
}

std::string FrancaGeneratorExtensions::getStubSourceFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "Stub.cpp";
}

std::string FrancaGeneratorExtensions::getStubHeaderFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "Stub.hpp";
}

std::string FrancaGeneratorExtensions::getStubSourcePath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getStubSourceFile(fInterface);
}

std::string FrancaGeneratorExtensions::getStubHeaderPath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getStubHeaderFile(fInterface);
}

std::string FrancaGeneratorExtensions::getStubDefaultHeaderFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "StubDefault" + getSkeletonNamePostfix() + ".hpp";
}

std::string FrancaGeneratorExtensions::getSkeletonNamePostfix()
{
    // return FPreferences::getInstance().getPreference(PreferenceConstants::getInstance().P_SKELETONPOSTFIX,
    // "Default");
    return "";
}

std::string FrancaGeneratorExtensions::getStubDefaultHeaderPath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getStubDefaultHeaderFile(fInterface);
}

std::string FrancaGeneratorExtensions::getStubDefaultSourceFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "StubDefault" + getSkeletonNamePostfix() + ".cpp";
}

std::string FrancaGeneratorExtensions::getStubDefaultSourcePath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           getStubDefaultSourceFile(fInterface);
}

std::string FrancaGeneratorExtensions::someipProxyHeaderFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "SomeIPProxy.hpp";
}

std::string FrancaGeneratorExtensions::someipProxyHeaderPath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           someipProxyHeaderFile(fInterface);
}

std::string FrancaGeneratorExtensions::someipProxySourceFile(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "SomeIPProxy.cpp";
}

std::string FrancaGeneratorExtensions::someipProxySourcePath(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getVersionPathPrefix(fInterface) +
           getDirectoryPath(std::dynamic_pointer_cast<BstIdl::FModel>((fInterface->getContainer()))) + "/" +
           someipProxySourceFile(fInterface);
}

void FrancaGeneratorExtensions::generateSelectiveBroadcastStubIncludes(
    const std::shared_ptr<BstIdl::FInterface> &fInterface, std::list<std::string> &generatedHeaders,
    std::list<std::string> &libraryHeaders)
{
    for (auto broadcast : fInterface->getBroadcasts())
    {
        if (!broadcast->isSelective())
        {
            addListItem(libraryHeaders, "unordered_set");
            break;
        }
    }
}

std::list<std::string> FrancaGeneratorExtensions::splitCamelCase(const std::string &str)
{
    std::list<int64_t> pos;
    for (auto it = str.begin(); it != str.end(); it++)
    {
        if (((*it) - 'A') >= 0 && ('Z' - (*it)) >= 0)
        {
            pos.push_back(it - str.begin());
        }
    }
    std::list<std::string> words;
    int64_t begin = pos.front();
    // if the first character is lowercase
    if (begin != 0)
    {
        words.push_back(str.substr(0, begin));
    }
    while (pos.size() > 1)
    {
        pos.pop_front();
        int64_t end = pos.front();
        words.push_back(str.substr(begin, end - begin));
        begin = end;
    }
    if (pos.size() == 1)
        words.push_back(str.substr(begin, str.size() - begin));
    // the whole world is lowercase
    if (words.empty())
    {
        words.push_back(str);
    }
    return words;
}

std::string FrancaGeneratorExtensions::getDefineName(const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    std::string definePrefix;
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement) != nullptr)
    {
        auto tc = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement);
        if (tc->getVersion() != nullptr)
        {
            definePrefix = "V" + std::to_string(tc->getVersion()->getMajor()) + "_";
        }
    }
    auto words = splitCamelCase(getElementName(fModelElement));
    std::string defineSuffix;
    while (words.size() != 0)
    {
        defineSuffix += "_" + words.front();
        words.pop_front();
    }
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement))
    {
        auto &mgr = BstIdl::FModelManager::getInstance();
        auto model = std::dynamic_pointer_cast<BstIdl::FModel>(fModelElement->getContainer());
        return definePrefix + getDefineName(model) + defineSuffix;
    }
    else
    {
        return definePrefix + getDefineName(std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement)) +
               defineSuffix;
    }
}

std::string FrancaGeneratorExtensions::getDefineName(const std::shared_ptr<BstIdl::FModel> &model)
{
    auto name = model->getName();
    while (name.find(".") != name.npos)
        name = name.replace(name.find("."), 1, "_");
    transform(name.begin(), name.end(), name.begin(), ::toupper);
    return name;
}

std::string FrancaGeneratorExtensions::generateVersionNamespaceBegin(
    const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    auto itsVersion = _tc->getVersion();
    if (itsVersion != nullptr && (itsVersion->getMajor() != 0 || itsVersion->getMinor() != 0))
    {
        return "\nnamespace v" + std::to_string(itsVersion->getMajor()) + " {";
    }
    return "";
}

std::shared_ptr<BstIdl::FInterface> FrancaGeneratorExtensions::getContainingInterface(
    const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    if (fModelElement->getContainer() == nullptr ||
        std::dynamic_pointer_cast<BstIdl::FModel>(fModelElement->getContainer()))
        return nullptr;
    if (std::dynamic_pointer_cast<BstIdl::FInterface>(fModelElement->getContainer()) != nullptr)
    {
        return std::dynamic_pointer_cast<BstIdl::FInterface>(fModelElement->getContainer());
    }
    return getContainingInterface(std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement->getContainer()));
}

std::string FrancaGeneratorExtensions::generateCases(const std::shared_ptr<BstIdl::FStructType> &fStructType,
                                                     const std::shared_ptr<BstIdl::FModelElement> &parent,
                                                     bool qualified)
{
    std::string itsCases = "case ";
    if (qualified && getContainingInterface(fStructType) != nullptr)
        itsCases = itsCases + getElementName(getContainingInterface(fStructType)) + "::";
    auto name = getElementName(fStructType);
    transform(name.begin(), name.end(), name.begin(), ::toupper);
    itsCases = itsCases + name + "_SERIAL:\n";
    std::list<std::shared_ptr<BstIdl::FStructType>> types;
    getDerivedFStructTypes(fStructType, types);
    for (auto derived : types)
    {
        itsCases = itsCases + generateCases(derived, parent, qualified);
    }
    return itsCases;
}

bool FrancaGeneratorExtensions::isComplex(std::string _typeName)
{
    if (_typeName == "bool" || _typeName == "int8" || _typeName == "int16" || _typeName == "int32" ||
        _typeName == "int64" || _typeName == "uint8" || _typeName == "uint16" || _typeName == "uint32" ||
        _typeName == "uint64")
    {
        return false;
    }
    return true;
}

std::string FrancaGeneratorExtensions::getConstantType(
    const std::shared_ptr<BstIdl::FInitializerExpression> &expression)
{
    if (std::dynamic_pointer_cast<BstIdl::FIntegerConstant>(expression))
        return std::dynamic_pointer_cast<BstIdl::FIntegerConstant>(expression)->toString();
    else
        return nullptr;
}

std::string FrancaGeneratorExtensions::getConstantValue(const std::shared_ptr<BstIdl::FModelElement> &expression)
{
    if (std::dynamic_pointer_cast<BstIdl::FConstantDef>(expression) != nullptr)
    {
        return getConstantType(std::dynamic_pointer_cast<BstIdl::FConstantDef>(expression)->getRhs());
    }
    else
    {
        return nullptr;
    }
}

std::string FrancaGeneratorExtensions::getEnumeratorValue(const std::shared_ptr<BstIdl::FExpression> &expression)
{
    if (std::dynamic_pointer_cast<BstIdl::FIntegerConstant>(expression) != nullptr)
        return std::dynamic_pointer_cast<BstIdl::FIntegerConstant>(expression)->toString();
    else if (std::dynamic_pointer_cast<BstIdl::FStringConstant>(expression) != nullptr)
        return std::dynamic_pointer_cast<BstIdl::FStringConstant>(expression)->toString();
    else if (std::dynamic_pointer_cast<BstIdl::FQualifiedElementRef>(expression) != nullptr)
        return getConstantValue(std::dynamic_pointer_cast<BstIdl::FQualifiedElementRef>(expression)->getElement());
    else
    {
        return nullptr;
    }
}

int64_t FrancaGeneratorExtensions::getMaximumEnumerationValue(
    const std::shared_ptr<BstIdl::FEnumerationType> &_enumeration)
{
    int64_t maximum = 0;
    for (auto literal : _enumeration->getEnumerators())
    {
        if (literal->getValue() != nullptr)
        {
            auto value = literal->getValue();
            int64_t literalValue = atoi(getEnumeratorValue(value).c_str());
            if (maximum < literalValue)
                maximum = literalValue;
        }
    }
    return maximum;
}

void FrancaGeneratorExtensions::setEnumerationValues(const std::shared_ptr<BstIdl::FEnumerationType> &_enumeration)
{
    int64_t currentValue = 0;
    std::list<std::string> predefineEnumValues;
    // collect all predefined enum values
    for (auto literal : _enumeration->getEnumerators())
    {
        if (literal->getValue().get() != nullptr)
        {
            predefineEnumValues.push_back(getEnumeratorValue(literal->getValue()));
        }
    }
    if (_enumeration->getBase() != nullptr)
    {
        auto base = _enumeration->getBase();
        setEnumerationValues(base);
        currentValue = getMaximumEnumerationValue(base) + 1;
    }
    for (auto literal : _enumeration->getEnumerators())
    {
        if (literal->getValue() == nullptr)
        {
            for (auto it : predefineEnumValues)
            {
                if (atoi(it.c_str()) == currentValue)
                {
                    currentValue += 1;
                }
            }
            auto obj = BstIdl::FFactory::getInstance().createFIntegerConstant();
            obj->setUnsignedVal(currentValue);
            literal->setValue(obj);
        }
        else
        {
            auto enumValue = getEnumeratorValue(literal->getValue());
            if (!enumValue.empty())
            {
                auto literalValue = atoi(enumValue.c_str());
                auto obj = BstIdl::FFactory::getInstance().createFIntegerConstant();
                obj->setUnsignedVal(literalValue);
                literal->setValue(obj);
            }
            else
            {
                auto obj = BstIdl::FFactory::getInstance().createFIntegerConstant();
                obj->setUnsignedVal(currentValue);
                literal->setValue(obj);
            }
        }
        currentValue += 1;
    }
}

std::shared_ptr<BstIdl::FBasicTypeId> FrancaGeneratorExtensions::getBackingType(
    const std::shared_ptr<BstIdl::FEnumerationType> &fEnumerationType,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    switch (deploymentAccessor->getEnumBackingType(fEnumerationType))
    {
    case EnumBackingType::UInt8:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::UINT8);
    case EnumBackingType::UInt16:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::UINT16);
    case EnumBackingType::UInt32:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::UINT32);
    case EnumBackingType::UInt64:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::UINT64);
    case EnumBackingType::Int8:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::INT8);
    case EnumBackingType::Int16:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::INT16);
    case EnumBackingType::Int32:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::INT32);
    case EnumBackingType::Int64:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::INT64);
    default:
        return BstIdl::FBasicTypeId::get(BstIdl::FBasicTypeId::UINT8);
    }
}

std::string FrancaGeneratorExtensions::getEnumPrefix()
{
    // return FPreferences::getInstance().getPreference(PreferenceConstants::getInstance().P_ENUMPREFIX, "");
    return "";
}

std::string FrancaGeneratorExtensions::getBaseType(const std::shared_ptr<BstIdl::FEnumerationType> &_enumeration,
                                                   const std::shared_ptr<BstIdl::FEnumerationType> &_other,
                                                   std::string _backingType)
{
    std::string baseType;
    if (_enumeration->getBase() != nullptr)
    {
        baseType = getElementName(_enumeration->getBase(), _other, false);
    }
    else
    {
        baseType = "CommonAPI::Enumeration<" + _backingType + ">";
    }
    return baseType;
}

std::list<std::string> FrancaGeneratorExtensions::getNamespaceAsList(const std::shared_ptr<BstIdl::FModel> &fModel)
{
    auto name = fModel->getName();
    std::list<std::string> list;
    size_t pos = 0;
    while (name.find(".") < (name.size() - 1))
    {
        list.push_back(name.substr(pos, name.find(".")));
        pos = name.find(".") + 1;
        name = name.substr(pos);
        pos = 0;
    }
    list.push_back(name);
    return list;
}

std::list<std::shared_ptr<BstIdl::FMethod>> FrancaGeneratorExtensions::getMethodsWithError(
    const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    std::map<std::string, std::shared_ptr<BstIdl::FMethod>> itsMethods;
    for (auto method : _interface->getMethods())
    {
        if (method->getErrors() != nullptr)
        {
            auto existing = itsMethods.find(method->getName());
            if (existing == itsMethods.end())
            {
                itsMethods[method->getName()] = method;
            }
            else
            {
                std::list<std::shared_ptr<BstIdl::FEnumerator>> itsAdditionals;

                for (auto e : method->getErrors()->getEnumerators())
                {
                    auto found = false;
                    for (auto f : itsMethods[method->getName()]->getErrors()->getEnumerators())
                    {
                        if (f->getName() == e->getName())
                            found = true;
                    }
                    if (!found)
                        itsAdditionals.emplace_back(e);
                }
                for (auto itsAdditional : itsAdditionals)
                {
                    addListItem(itsMethods[method->getName()]->getErrors()->getEnumerators(), std::move(itsAdditional));
                }
            }
        }
    }
    std::list<std::shared_ptr<BstIdl::FMethod>> methods;
    for (auto it : itsMethods)
    {
        methods.emplace_back(it.second);
    }
    return methods;
}

std::string FrancaGeneratorExtensions::getInterfaceVersion(const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    return "v" + std::to_string(_interface->getVersion()->getMajor()) + "_" +
           std::to_string(_interface->getVersion()->getMinor());
}

std::string FrancaGeneratorExtensions::getFullyQualifiedNameWithVersion(
    const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    return getFullyQualifiedName(_interface) + ":" + getInterfaceVersion(_interface);
}

std::string FrancaGeneratorExtensions::getFullyQualifiedCppName(
    const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    if (!fModelElement || !fModelElement->getContainer())
        return "";
    if (auto ptr = std::dynamic_pointer_cast<BstIdl::FModel>(fModelElement->getContainer()))
    {
        std::string containerName = ptr->getName();
        std::string prefix = "::";
        if (auto tc = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement))
        {
            if (tc->getVersion())
                prefix = getVersionPrefix(tc);
        }
        std::string ret = prefix + containerName + "::" + getElementName(fModelElement);
        return replace_all(ret, ".", "::");
    }
    std::string ret =
        getFullyQualifiedCppName(std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement->getContainer())) +
        "::" + getElementName(fModelElement);
    return replace_all(ret, ".", "::");
}

std::string FrancaGeneratorExtensions::generateNamespaceEndDeclaration(const std::shared_ptr<BstIdl::FModel> &model)
{
    std::list<std::string> list = getNamespaceAsList(model);
    std::string str;
    reverse(list.begin(), list.end());
    for (auto subnamespace : list)
    {
        str += "} // namespace " + subnamespace + "\n";
    }
    return str;
}

std::string FrancaGeneratorExtensions::generateVersionNamespaceEnd(const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    auto itsVersion = _tc->getVersion();
    if (itsVersion != nullptr && (itsVersion->getMajor() != 0 || itsVersion->getMinor() != 0))
    {
        return "} // namespace v" + std::to_string(itsVersion->getMajor()) + "\n";
    }
    return "";
}

std::string FrancaGeneratorExtensions::generateMajorVersionNamespace(
    const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    std::string str;
    auto itsVersion = _tc->getVersion();
    if (itsVersion != nullptr && (itsVersion->getMajor() != 0 || itsVersion->getMinor() != 0))
    {
        str += "// Compatibility\nnamespace v" + std::to_string(itsVersion->getMajor()) + "_" +
               std::to_string(itsVersion->getMinor()) + "= v" + std::to_string(itsVersion->getMajor()) + ";\n";
    }
    return str;
}

std::string FrancaGeneratorExtensions::generateDeploymentNamespaceEnd(
    const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    return "} // namespace " + getElementName(_tc) + "_";
}

std::string FrancaGeneratorExtensions::generateVariantComparators(
    const std::shared_ptr<BstIdl::FTypeCollection> &fTypes)
{
    std::string str;
    for (auto type : fTypes->getTypes())
    {
        if (std::dynamic_pointer_cast<BstIdl::FUnionType>(type) != nullptr)
        {
            for (auto base : getBaseList(std::dynamic_pointer_cast<BstIdl::FUnionType>(type)))
            {
                str += generateComparatorImplementation(std::dynamic_pointer_cast<BstIdl::FUnionType>(type), base);
            }
        }
    }
    return str;
}

bool FrancaGeneratorExtensions::hasAttributes(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return (!fInterface->getAttributes().empty());
}

bool FrancaGeneratorExtensions::hasBroadcasts(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return (!fInterface->getBroadcasts().empty());
}

bool FrancaGeneratorExtensions::hasSelectiveBroadcasts(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    for (auto it : fInterface->getBroadcasts())
    {
        if (it->isSelective())
            return true;
    }
    return false;
}

std::string FrancaGeneratorExtensions::getProxyBaseClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "ProxyBase";
}

std::string FrancaGeneratorExtensions::getClassName(const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    auto name = toFirstUpper(getElementName(fAttribute));
    name += "Attribute";
    return name;
}

std::string FrancaGeneratorExtensions::getClassName(const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    auto name = toFirstUpper(getElementName(fBroadcast));
    if (fBroadcast->isSelective())
    {
        name = name + "Selective";
    }
    name += "Event";
    return name;
}

std::list<std::shared_ptr<BstIdl::FUnionType>> FrancaGeneratorExtensions::getBaseList(
    const std::shared_ptr<BstIdl::FUnionType> &fUnionType)
{
    std::list<std::shared_ptr<BstIdl::FUnionType>> baseList;
    auto currentBase = fUnionType->getBase();
    while (currentBase != nullptr)
    {
        baseList.emplace_back(currentBase);
        currentBase = currentBase->getBase();
    }
    return baseList;
}
std::list<std::string> FrancaGeneratorExtensions::getElementTypeNames(const std::shared_ptr<BstIdl::FUnionType> &fUnion)
{
    std::list<std::string> names;
    auto rev = fUnion->getElements();
    auto parent = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fUnion->getContainer());
    for (auto element : rev)
    {
        std::string lName = "";
        if (element->getType()->getPredefined() != nullptr)
        {
            lName = getVersionPrefix(parent);
            auto list = getNamespaceAsList(std::dynamic_pointer_cast<BstIdl::FModel>(parent->getContainer()));
            for (auto it : list)
            {
                lName += it + "::";
            }
            lName += getTypeName(element, fUnion, true);
        }
        else
        {
            lName = getTypeName(element, fUnion, false);
        }
        names.emplace_back(lName);
    }
    if (fUnion->getBase() != nullptr)
    {
        for (auto base : getElementTypeNames(fUnion->getBase()))
        {
            names.emplace_back(base);
        }
    }
    std::reverse(names.begin(), names.end());
    return names;
}

std::string FrancaGeneratorExtensions::generateVariantComnparatorIf(std::list<std::string> list)
{
    int counter = 1;
    std::string ret = "";
    for (auto it : list)
    {
        if (counter > 1)
        {
            ret += "else ";
        }
        ret += "if (_rhs.getValueType() == " + std::to_string(counter) + ") { \n" + "    " + it + " a = _lhs.get<" +
               it + ">(); \n" + "    " + it + " b = _rhs.get<" + it + ">(); \n" + "    " + "return (a == b); \n" +
               "}\n";
        counter = counter + 1;
    }
    return ret;
}

std::string FrancaGeneratorExtensions::generateComparatorImplementation(
    const std::shared_ptr<BstIdl::FUnionType> &_derived, const std::shared_ptr<BstIdl::FUnionType> &_base)
{
    auto unionTypeName = getElementName(_derived, nullptr, true);
    auto unionBaseTypeName = getElementName(_base, nullptr, true);
    auto unionTypeContainerName =
        getTypeCollectionName(std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_derived->getContainer()), nullptr);
    auto unionBaseTypeContainerName =
        getTypeCollectionName(std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_base->getContainer()), nullptr);
    std::string str = "\ninline bool operator==(";
    str += "\n\t\tconst " + unionTypeContainerName + "::" + unionTypeName + " &_lhs,";
    str += "\n\t\tconst " + unionBaseTypeContainerName + "::" + unionBaseTypeName + " &_rhs) {";
    str += "\n\tif (_lhs.getValueType() == _rhs.getValueType()) {";
    auto list = getElementTypeNames(_base);
    str += "\n\t\t" + generateVariantComnparatorIf(list) + "\n\t}\n\treturn false;\n}\n";
    return str;
}

std::list<std::string> FrancaGeneratorExtensions::getNamespaceAsList(
    const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    std::list<std::string> namespaceList;
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement) != nullptr)
    {
        auto model = std::dynamic_pointer_cast<BstIdl::FModel>(
            std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement)->getContainer());
        namespaceList = getNamespaceAsList(model);
    }
    else
    {
        auto element = std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement->getContainer());
        namespaceList = getNamespaceAsList(element);
        namespaceList.emplace_back(getElementName(element));
    }
    return namespaceList;
}

std::string FrancaGeneratorExtensions::generateNamespaceBeginDeclaration(const std::shared_ptr<BstIdl::FModel> &model)
{
    std::string str;
    for (auto subnamespace : getNamespaceAsList(model))
    {
        str += "\nnamespace " + subnamespace + " {";
    }
    return str;
}

bool FrancaGeneratorExtensions::isTheSameVersion(const std::shared_ptr<BstIdl::FVersion> _mine,
                                                 const std::shared_ptr<BstIdl::FVersion> _other)
{
    return ((_mine == nullptr && _other == nullptr) ||
            (_mine != nullptr && _other != nullptr && _mine->getMajor() == _other->getMajor() &&
             _mine->getMinor() == _other->getMinor()));
}

std::string FrancaGeneratorExtensions::getVersionPrefix(const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    std::string prefix = "::";
    auto itsVersion = _tc->getVersion();
    if (itsVersion != nullptr && (itsVersion->getMajor() != 0 || itsVersion->getMinor() != 0))
    {
        prefix += "v" + std::to_string(itsVersion->getMajor()) + "::";
    }
    return prefix;
}

std::string FrancaGeneratorExtensions::getContainerName(const std::shared_ptr<BstIdl::FObject> &_container)
{
    std::string name;
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_container) != nullptr)
    {
        name = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_container)->getName();
        if (name.empty())
            name = "__Anonymous__";
    }
    else
    {
        name = _container->getName();
    }
    return name;
}

std::string FrancaGeneratorExtensions::getFullName(const std::shared_ptr<BstIdl::FObject> &_me)
{
    std::string name;
    if (std::dynamic_pointer_cast<BstIdl::FModelElement>(_me) != nullptr)
    {
        std::string prefix;
        std::shared_ptr<BstIdl::FObject> container = _me;
        std::list<std::string> names;
        while (container != nullptr)
        {
            if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(container) != nullptr)
                prefix = getVersionPrefix(std::dynamic_pointer_cast<BstIdl::FTypeCollection>(container));
            auto containerName = getContainerName(container);
            if (!containerName.empty())
                names.emplace_front(containerName);
            container = container->getContainer();
        }
        name = prefix + join(names, "::");
    }
    replace_all(name, ".", "::");
    return name;
}

std::string FrancaGeneratorExtensions::getTypeCollectionName(const std::shared_ptr<BstIdl::FTypeCollection> &_me,
                                                             const std::shared_ptr<BstIdl::FTypeCollection> &_other)
{
    if (!_me)
        return "";
    auto &mgr = BstIdl::FModelManager::getInstance();
    if (_other == nullptr || _me->getContainer() != _other->getContainer() ||
        isTheSameVersion(_me->getVersion(), _other->getVersion()))
    {
        return getFullName(_me);
    }
    return getContainerName(_me);
}

std::shared_ptr<BstIdl::FObject> FrancaGeneratorExtensions::getCommonContainer(
    const std::shared_ptr<BstIdl::FObject> &_me, const std::shared_ptr<BstIdl::FObject> &_other)
{
    if (_other == nullptr)
        return nullptr;
    if (_me == _other)
        return _me;
    auto me = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_me);
    auto other = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_other);
    if (me && other)
    {
        if (me->getContainer() == _other->getContainer() &&
            ((me->getVersion() == nullptr && other->getVersion() == nullptr) ||
             (me->getVersion() != nullptr && other->getVersion() != nullptr &&
              me->getVersion()->getMajor() == other->getVersion()->getMajor() &&
              me->getVersion()->getMinor() == other->getVersion()->getMinor())))
        {
            return _me;
        }
    }
    return getCommonContainer(_me, _other->getContainer());
}

std::string FrancaGeneratorExtensions::getPartialName(const std::shared_ptr<BstIdl::FModelElement> &_me,
                                                      const std::shared_ptr<BstIdl::FObject> &_until)
{
    std::string name = getContainerName(_me);
    auto container = _me->getContainer();
    while (container != nullptr && container != _until)
    {
        if (std::dynamic_pointer_cast<BstIdl::FModel>(container) != nullptr ||
            std::dynamic_pointer_cast<BstIdl::FModelElement>(container))
        {
            name = getContainerName(container) + "::" + name;
        }
        container = container->getContainer();
    }
    while (name.find(".") != name.npos)
        name = name.replace(name.find("."), 1, "::");
    return name;
}

std::shared_ptr<BstIdl::FVersion> getRVersion(std::shared_ptr<BstIdl::FObject> element)
{
    while (element)
    {
        if (auto ptr = std::dynamic_pointer_cast<BstIdl::FTypeCollection>(element))
            return ptr->getVersion();
        else
            element = element->getContainer();
    }
    return nullptr;
}

std::string FrancaGeneratorExtensions::getElementName(const std::shared_ptr<BstIdl::FModelElement> &_me,
                                                      const std::shared_ptr<BstIdl::FModelElement> &_other,
                                                      bool _isOther)
{
    if (_other == nullptr)
        return getContainerName(_me);
    std::shared_ptr<BstIdl::FVersion> myVersion = getRVersion(_me);
    std::shared_ptr<BstIdl::FVersion> otherVersion = getRVersion(_other);
    if (isTheSameVersion(myVersion, otherVersion))
    {
        auto myContainer = _me->getContainer();
        auto otherContainer = _other->getContainer();
        if (myContainer == otherContainer)
        {
            std::string name = getContainerName(_me);
            if (_isOther)
            {
                name = getContainerName(myContainer) + "::" + getContainerName(_me);
            }
            return name;
        }
        if (getCommonContainer(myContainer, otherContainer))
        {
            return getPartialName(_me, myContainer->getContainer());
        }
        if (getCommonContainer(otherContainer, myContainer))
        {
            std::string name = getContainerName(_me);
            if (_isOther)
            {
                name = getContainerName(myContainer) + "::" + name;
            }
            return name;
        }
    }
    return getFullName(_me);
}

std::string FrancaGeneratorExtensions::getPrimitiveTypeName(const std::shared_ptr<BstIdl::FBasicTypeId> &fBasicTypeId)
{
    auto value = fBasicTypeId->getValue();
    switch (value)
    {
    case BstIdl::FBasicTypeId::BOOLEAN:
        return "bool";
    case BstIdl::FBasicTypeId::INT8:
        return "int8_t";
    case BstIdl::FBasicTypeId::UINT8:
        return "uint8_t";
    case BstIdl::FBasicTypeId::INT16:
        return "int16_t";
    case BstIdl::FBasicTypeId::UINT16:
        return "uint16_t";
    case BstIdl::FBasicTypeId::INT32:
        return "int32_t";
    case BstIdl::FBasicTypeId::UINT32:
        return "uint32_t";
    case BstIdl::FBasicTypeId::INT64:
        return "int64_t";
    case BstIdl::FBasicTypeId::UINT64:
        return "uint64_t";
    case BstIdl::FBasicTypeId::FLOAT:
        return "float";
    case BstIdl::FBasicTypeId::DOUBLE:
        return "double";
    case BstIdl::FBasicTypeId::STRING:
        return "std::string";
    case BstIdl::FBasicTypeId::BYTE_BUFFER:
        return "CommonAPI::ByteBuffer";
    default:
        std::cerr << "Unsupported basic type: " << fBasicTypeId->getName() << std::endl;
        return std::string();
    }
}

std::string FrancaGeneratorExtensions::getElementType(const std::shared_ptr<BstIdl::FTypeRef> &_typeRef,
                                                      const std::shared_ptr<BstIdl::FModelElement> &_container,
                                                      bool _isOther)
{
    std::string typeName;
    if (_typeRef->getDerived())
    {
        typeName = getElementName(_typeRef->getDerived(), _container, _isOther);
    }
    else if (_typeRef->getInterval())
    {
        auto interval = _typeRef->getInterval();
        typeName = "CommonAPI::RangedInteger<" + std::to_string(interval->getLowerBound()) + ", " +
                   std::to_string(interval->getUpperBound()) + ">";
    }
    else if (_typeRef->getPredefined())
    {
        typeName = getPrimitiveTypeName(_typeRef->getPredefined());
    }
    return typeName;
}

std::string FrancaGeneratorExtensions::getFullyQualifiedName(
    const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    auto &mgr = BstIdl::FModelManager::getInstance();
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement))
        return std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement)->getContainer()->getName() + "." +
               getElementName(fModelElement);
    else
        return getFullyQualifiedName(std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement->getContainer())) +
               "." + getElementName(fModelElement);
}

std::string FrancaGeneratorExtensions::putFTypeObject(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    std::string str;
    if (fStructType->getBase() != nullptr)
        str += putFTypeObject(fStructType->getBase());
    str += getFullyQualifiedName(fStructType) + "FStructType";
    for (auto it : fStructType->getElements())
    {
        str += getElementName(it);
    }
    return str;
}

uint64_t FrancaGeneratorExtensions::getSerialId(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    auto str = putFTypeObject(fStructType);
    return hash(str);
}

bool FrancaGeneratorExtensions::hasDerivedTypes(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    std::list<std::shared_ptr<BstIdl::FStructType>> struct_types;
    getDerivedFStructTypes(fStructType, struct_types);
    return !struct_types.empty();
}

void FrancaGeneratorExtensions::getDerivedFStructTypes(const std::shared_ptr<BstIdl::FStructType> &fStructType,
                                                       std::list<std::shared_ptr<BstIdl::FStructType>> &struct_types)
{
    if (!fStructType)
        return;

    BstIdl::FModelManager &mgr = BstIdl::FModelManager::getInstance();
    auto parent = fStructType->getContainer();
    if (!parent)
        return;
    auto model = std::dynamic_pointer_cast<BstIdl::FModel>(parent->getContainer());
    if (model)
        mgr.getDerivedFStructTypes(model, fStructType, struct_types);
}

std::string FrancaGeneratorExtensions::createSerials(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    std::string serials = "static const CommonAPI::Serial ";
    auto name = getElementName(fStructType);
    transform(name.begin(), name.end(), name.begin(), ::toupper);
    serials += name + fmt::format("_SERIAL = {0:#x}", getSerialId(fStructType)) + ";\n";
    std::list<std::shared_ptr<BstIdl::FStructType>> struct_types;
    getDerivedFStructTypes(fStructType, struct_types);
    for (auto it : struct_types)
    {
        serials = serials + createSerials(it);
    }
    return serials;
}

std::string FrancaGeneratorExtensions::generateDummyValue(const std::shared_ptr<BstIdl::FTypeRef> &typeRef)
{
    std::string retval;
    if (typeRef->getDerived() == nullptr && typeRef->getPredefined())
    {
        switch (typeRef->getPredefined()->getValue())
        {
        case BstIdl::FBasicTypeId::BOOLEAN:
            retval = "false";
            break;
        case BstIdl::FBasicTypeId::INT8:
            retval = "0";
            break;
        case BstIdl::FBasicTypeId::UINT8:
            retval = "0u";
            break;
        case BstIdl::FBasicTypeId::INT16:
            retval = "0";
            break;
        case BstIdl::FBasicTypeId::UINT16:
            retval = "0u";
            break;
        case BstIdl::FBasicTypeId::INT32:
            retval = "0";
            break;
        case BstIdl::FBasicTypeId::UINT32:
            retval = "0ul";
            break;
        case BstIdl::FBasicTypeId::INT64:
            retval = "0";
            break;
        case BstIdl::FBasicTypeId::UINT64:
            retval = "0ull";
            break;
        case BstIdl::FBasicTypeId::FLOAT:
            retval = "0.0f";
            break;
        case BstIdl::FBasicTypeId::DOUBLE:
            retval = "0.0";
            break;
        case BstIdl::FBasicTypeId::STRING:
            retval = "\"\"";
            break;
        case BstIdl::FBasicTypeId::BYTE_BUFFER:
            retval = "CommonAPI::ByteBuffer()";
            break;
        default:
            retval = "";
            break;
        }
    }
    return retval;
}

bool FrancaGeneratorExtensions::hasPolymorphicBase(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    if (fStructType->isPolymorphic())
    {
        return true;
    }
    return fStructType->getBase() != nullptr && fStructType->getBase()->isPolymorphic();
}

std::string FrancaGeneratorExtensions::getTypeName(const std::shared_ptr<BstIdl::FTypedElement> &_element,
                                                   const std::shared_ptr<BstIdl::FModelElement> &_source, bool _isOther)
{
    std::string typeName = getElementType(_element->getType(), _source, _isOther);
    auto ptr = std::dynamic_pointer_cast<BstIdl::FStructType>(_element->getType()->getDerived());
    if (ptr && ptr->isPolymorphic())
        typeName = "std::shared_ptr<" + typeName + ">";
    if (_element->isArray())
        typeName = "std::vector<" + typeName + ">";
    return typeName;
}

std::list<std::shared_ptr<BstIdl::FField>> FrancaGeneratorExtensions::getAllElements(
    const std::shared_ptr<BstIdl::FUnionType> &_union)
{
    if (_union->getBase() == nullptr)
        return _union->getElements();
    auto elements = getAllElements(_union->getBase());
    for (auto it : _union->getElements())
    {
        addListItem(elements, it);
    }
    return elements;
}

std::list<std::shared_ptr<BstIdl::FField>> FrancaGeneratorExtensions::getAllElements(
    const std::shared_ptr<BstIdl::FStructType> &_struct)
{
    if (_struct->getBase() == nullptr)
        return _struct->getElements();
    auto elements = getAllElements(_struct->getBase());
    for (auto it : _struct->getElements())
    {
        addListItem(elements, it);
    }
    return elements;
}

uint32_t FrancaGeneratorExtensions::hash(const std::string &word)
{
    size_t str_len = word.length();
    const char *c_origin = word.c_str();
    const uint32_t seed = 0;
    auto hash = murmur3_32(c_origin, sizeof(c_origin) - 1, seed);
    return hash;
}

uint32_t FrancaGeneratorExtensions::murmur3_32(const char *key, uint32_t len, uint32_t seed)
{
    static const uint32_t c1 = 0xcc9e2d51;
    static const uint32_t c2 = 0x1b873593;
    static const uint32_t r1 = 15;
    static const uint32_t r2 = 13;
    static const uint32_t m = 5;
    static const uint32_t n = 0xe6546b64;

    uint32_t hash = seed;

    const int nblocks = len / 4;
    const uint32_t *blocks = (const uint32_t *)key;
    int i;
    uint32_t k;
    for (i = 0; i < nblocks; i++)
    {
        k = blocks[i];
        k *= c1;
        k = ROT32(k, r1);
        k *= c2;

        hash ^= k;
        hash = ROT32(hash, r2) * m + n;
    }

    const uint8_t *tail = (const uint8_t *)(key + nblocks * 4);
    uint32_t k1 = 0;

    switch (len & 3)
    {
    case 3:
        k1 ^= tail[2] << 16;
    case 2:
        k1 ^= tail[1] << 8;
    case 1:
        k1 ^= tail[0];

        k1 *= c1;
        k1 = ROT32(k1, r1);
        k1 *= c2;
        hash ^= k1;
    }

    hash ^= len;
    hash ^= (hash >> 16);
    hash *= 0x85ebca6b;
    hash ^= (hash >> 13);
    hash *= 0xc2b2ae35;
    hash ^= (hash >> 16);

    return hash;
}

bool FrancaGeneratorExtensions::isSignedBackingType(std::string _backingType)
{
    return (_backingType == "int8_t" || _backingType == "int16_t" || _backingType == "int32_t" ||
            _backingType == "int64_t");
}

bool FrancaGeneratorExtensions::hasError(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    if (fMethod->getErrorEnum() != nullptr || fMethod->getErrors() != nullptr)
    {
        return true;
    }
    return false;
}

std::string FrancaGeneratorExtensions::getErrorName(const std::shared_ptr<BstIdl::FEnumerationType> &fMethodErrors)
{
    if (std::dynamic_pointer_cast<BstIdl::FMethod>(fMethodErrors->getContainer()) == nullptr)
    {
        std::cout << "Not FMethod errors\n";
        return "";
    }
    return getElementName(std::dynamic_pointer_cast<BstIdl::FMethod>(fMethodErrors->getContainer())) + "Error";
}

std::string FrancaGeneratorExtensions::getRelativeNameReference(
    const std::shared_ptr<BstIdl::FModelElement> &destination, const std::shared_ptr<BstIdl::FObject> &source)
{
    auto nameReference = getElementName(destination);
    std::list<std::string> subnamespaceList;
    if (destination != nullptr && destination->getContainer() != nullptr && destination->getContainer() == source)
    {
        subnamespaceList = getSubnamespaceList(destination, source);
    }
    if (!subnamespaceList.empty())
    {
        std::string preName;
        for (auto it : subnamespaceList)
        {
            preName += it + "::";
        }
        nameReference = preName + nameReference;
    }
    return nameReference;
}

std::string FrancaGeneratorExtensions::getErrorNameReference(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                             const std::shared_ptr<BstIdl::FObject> &source)
{
    if (!hasError(fMethod))
    {
        std::cout << "FMethod has no error: " + fMethod->getName() + "\n";
        return "";
    }
    if (fMethod->getErrorEnum() != nullptr)
    {
        auto errorEnum = fMethod->getErrorEnum();
        return getElementName(errorEnum, fMethod, true);
    }
    auto errors = fMethod->getErrors();
    auto errorNameReference = getErrorName(errors);
    errorNameReference =
        getRelativeNameReference(std::dynamic_pointer_cast<BstIdl::FModelElement>(fMethod->getContainer()), source) +
        "::" + errorNameReference;
    return errorNameReference;
}

std::string FrancaGeneratorExtensions::generateASyncTypedefSignature(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string signature = "const CommonAPI::CallStatus&";
    if (hasError(fMethod))
    {
        signature = signature + ", const " + getErrorNameReference(fMethod, fMethod->getContainer()) + "&";
    }
    if (!fMethod->getOutArgs().empty())
    {
        for (auto it : fMethod->getOutArgs())
        {
            signature += ", const " + getTypeName(it, fMethod, true) + "&";
        }
    }
    return signature;
}

std::string FrancaGeneratorExtensions::getAsyncCallbackClassName(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    if (needsMangling(fMethod))
    {
        return getMangledAsyncCallbackClassName(fMethod);
    }
    else
    {
        return getBasicAsyncCallbackClassName(fMethod);
    }
}

bool FrancaGeneratorExtensions::needsMangling(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    for (auto otherMethod : getContainingInterface(fMethod)->getMethods())
    {
        if (otherMethod != fMethod &&
            getBasicAsyncCallbackClassName(otherMethod) == getBasicAsyncCallbackClassName(fMethod) &&
            generateASyncTypedefSignature(otherMethod) != generateASyncTypedefSignature(fMethod))
        {
            return true;
        }
    }
    return false;
}

std::string FrancaGeneratorExtensions::generateGetMethodDefinition(
    const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    std::string definition = generateGetMethodDefinitionWithin(fAttribute, "");
    return definition;
}

std::string FrancaGeneratorExtensions::generateGetMethodDefinitionWithin(
    const std::shared_ptr<BstIdl::FAttribute> &fAttribute, std::string parentClassName)
{
    auto definition = getClassName(fAttribute) + "& ";
    if (!parentClassName.empty())
    {
        definition = parentClassName + "::" + definition + parentClassName + "::";
    }
    definition = definition + "get" + getClassName(fAttribute) + "()";
    if (FTypeGenerator::isdeprecated(fAttribute->getComment()))
        definition = "COMMONAPI_DEPRECATED " + definition;
    return definition;
}

std::string FrancaGeneratorExtensions::generateGetMethodDefinition(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    std::string definition = generateGetMethodDefinitionWithin(fBroadcast, "");
    return definition;
}

std::string FrancaGeneratorExtensions::generateGetMethodDefinitionWithin(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast, std::string parentClassName)
{
    auto definition = getClassName(fBroadcast) + "& ";
    if (!parentClassName.empty())
    {
        definition = parentClassName + "::" + definition + parentClassName + "::";
    }
    definition = definition + "get" + getClassName(fBroadcast) + "()";
    if (FTypeGenerator::isdeprecated(fBroadcast->getComment()))
        definition = "COMMONAPI_DEPRECATED " + definition;
    return definition;
}

std::string FrancaGeneratorExtensions::generateAsyncDefinition(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                               bool _isDefault)
{
    return generateAsyncDefinitionWithin(fMethod, "", _isDefault);
}

std::string FrancaGeneratorExtensions::generateAsyncDefinitionWithin(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                                     std::string parentClassName, bool _isDefault)
{
    std::string definition = "std::future<CommonAPI::CallStatus> ";
    if (FTypeGenerator::isdeprecated(fMethod->getComment()))
    {
        definition = "COMMONAPI_DEPRECATED " + definition;
    }
    if (!parentClassName.empty())
    {
        definition = definition + parentClassName + "::";
    }
    definition =
        definition + getElementName(fMethod) + "Async(" + generateAsyncDefinitionSignature(fMethod, _isDefault) + ")";
    return definition;
}

std::string FrancaGeneratorExtensions::generateAsyncDefinitionSignature(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                                        bool _isDefault)
{
    std::list<std::string> signature;
    for (auto it : fMethod->getInArgs())
    {
        signature.emplace_back("const " + getTypeName(it, fMethod, true) + " &_" + getElementName(it));
    }

    if (_isDefault)
    {
        signature.emplace_back(getAsyncCallbackClassName(fMethod) + " _callback = nullptr");
        signature.emplace_back("const CommonAPI::CallInfo *_info = nullptr");
    }
    else
    {
        signature.emplace_back(getAsyncCallbackClassName(fMethod) + " _callback");
        signature.emplace_back("const CommonAPI::CallInfo *_info");
    }

    return join(signature, ", ");
}

std::string FrancaGeneratorExtensions::generateDefinitionSignature(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                                   bool _isDefault)
{
    std::list<std::string> signature;
    for (auto inArg : fMethod->getInArgs())
        signature.emplace_back(getTypeName(inArg, fMethod, true) + " _" + getElementName(inArg));

    signature.emplace_back("CommonAPI::CallStatus &_internalCallStatus");
    if (hasError(fMethod))
        signature.emplace_back(getErrorNameReference(fMethod, fMethod->getContainer()) + " &_error");
    for (auto outArg : fMethod->getOutArgs())
        signature.emplace_back(getTypeName(outArg, fMethod, true) + " &_" + getElementName(outArg));
    if (!fMethod->isFireAndForget())
    {
        if (_isDefault)
            signature.emplace_back("const CommonAPI::CallInfo *_info = nullptr");
        else
            signature.emplace_back("const CommonAPI::CallInfo *_info");
    }
    return join(signature, ", ");
}

std::string FrancaGeneratorExtensions::generateDefinition(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                          bool _isDefault)
{
    return generateDefinitionWithin(fMethod, "", _isDefault);
}

std::string FrancaGeneratorExtensions::generateDefinitionWithin(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                                std::string parentClassName, bool _isDefault)
{
    std::string definition = "void ";
    if (FTypeGenerator::isdeprecated(fMethod->getComment()))
    {
        definition = "COMMONAPI_DEPRECATED " + definition;
    }
    if (parentClassName != "" && (!parentClassName.empty()))
        definition = definition + parentClassName + "::";
    definition += getElementName(fMethod) + "(" + generateDefinitionSignature(fMethod, _isDefault) + ")";
    return definition;
}

std::string FrancaGeneratorExtensions::proxyManagerGetterName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "getProxyManager" + getElementName(fInterface);
}

std::string FrancaGeneratorExtensions::getProxyClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "Proxy";
}

bool FrancaGeneratorExtensions::supportsValidation(const std::shared_ptr<BstIdl::FTypeRef> &fTtypeRef)
{
    return (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(fTtypeRef->getDerived()) != nullptr);
}

std::string FrancaGeneratorExtensions::generateDummyArgumentInitializations(
    const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string retval;
    for (auto list_element : fMethod->getOutArgs())
    {
        retval += getTypeName(list_element, fMethod, true) + " " + getElementName(list_element);
        retval += generateDummyArgumentInitialization(list_element->getType(), list_element, fMethod);
        retval += ";\n";
    }
    return retval;
}

std::string FrancaGeneratorExtensions::generateDummyArgumentDefinitions(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string definition;
    if (hasError(fMethod))
    {
        definition = getErrorNameReference(fMethod, fMethod->getContainer()) + " error;\n";
    }
    if (!fMethod->getOutArgs().empty())
    {
        definition = definition + generateDummyArgumentInitializations(fMethod);
    }
    return definition;
}

std::string FrancaGeneratorExtensions::generateDummyArgumentList(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string arguments = "";
    if (hasError(fMethod))
        arguments = "error";
    if (!fMethod->getOutArgs().empty())
    {
        if (arguments != "")
            arguments = arguments + ", ";
        for (auto it : fMethod->getOutArgs())
        {
            if (it == fMethod->getOutArgs().front())
                arguments += getElementName(it);
            else
            {
                arguments += ", " + getElementName(it);
            }
        }
    }
    return arguments;
}

std::string FrancaGeneratorExtensions::generateCppNamespace(const std::shared_ptr<BstIdl::FModel> &fModel)
{
    std::string str;
    for (auto it : getNamespaceAsList(fModel))
    {
        str += it + "::";
    }
    return str;
}

std::string FrancaGeneratorExtensions::getStubAdapterClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "StubAdapter";
}

bool FrancaGeneratorExtensions::isObservable(const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    return (!fAttribute->isNoSubscriptions());
}

std::string FrancaGeneratorExtensions::getStubAdapterClassFireChangedMethodName(
    const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    auto name = toFirstUpper(getElementName(fAttribute));
    return "fire" + name + "AttributeChanged";
}

std::string FrancaGeneratorExtensions::getStubAdapterClassFireSelectiveMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    auto name = toFirstUpper(getElementName(fBroadcast));
    return "fire" + name + "Selective";
}

std::string FrancaGeneratorExtensions::generateFireSelectiveSignatur(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast, const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    std::string signature = "const std::shared_ptr<CommonAPI::ClientId> _client";
    if (!fBroadcast->getOutArgs().empty())
    {
        for (auto it : fBroadcast->getOutArgs())
        {
            signature += ", const " + getTypeName(it, fInterface, true) + " &_" + getElementName(it);
        }
    }
    return signature;
}

std::string FrancaGeneratorExtensions::getStubAdapterClassSendSelectiveMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    auto name = toFirstUpper(getElementName(fBroadcast));
    return "send" + name + "Selective";
}

std::string FrancaGeneratorExtensions::getSubscribeSelectiveMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    return "subscribeFor" + getElementName(fBroadcast) + "Selective";
}

std::string FrancaGeneratorExtensions::getUnsubscribeSelectiveMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    return "unsubscribeFrom" + getElementName(fBroadcast) + "Selective";
}

bool FrancaGeneratorExtensions::isErrorType(const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
                                            const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    return deploymentAccessor->getBroadcastType(fBroadcast) == BroadcastType::error;
}

bool FrancaGeneratorExtensions::isErrorType(const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
                                            const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                            const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (isErrorType(fBroadcast, deploymentAccessor) && !deploymentAccessor->getErrors(fMethod).empty())
    {
        for (auto it : deploymentAccessor->getErrors(fMethod))
        {
            if (it == getElementName(fBroadcast))
                return true;
        }
    }
    return false;
}

std::string FrancaGeneratorExtensions::getStubAdapterClassFireEventMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    std::string str = toFirstUpper(getElementName(fBroadcast));
    str = "fire" + str + "Event";
    return str;
}

std::string FrancaGeneratorExtensions::stubRegisterManagedMethod(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "bool " + stubRegisterManagedName(fInterface) + "(std::shared_ptr< " + getStubFullClassName(fInterface) +
           ">, const std::string&)";
}

std::string FrancaGeneratorExtensions::stubRegisterManagedName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "registerManagedStub" + getElementName(fInterface);
}

std::string FrancaGeneratorExtensions::getStubFullClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getFullName(fInterface) + "Stub";
}

std::string FrancaGeneratorExtensions::getStubClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "Stub";
}

std::string FrancaGeneratorExtensions::stubDeregisterManagedName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "deregisterManagedStub" + getElementName(fInterface);
}

std::string FrancaGeneratorExtensions::stubManagedSetGetterName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "get" + getElementName(fInterface) + "Instances";
}

std::string FrancaGeneratorExtensions::getStubClassLockMethodName(const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    auto name = toFirstUpper(getElementName(fAttribute));
    return "lock" + name + "Attribute";
}

std::string FrancaGeneratorExtensions::getStubAdapterClassSubscriberListPropertyName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    auto name = toFirstUpper(getElementName(fBroadcast));
    return "subscribersFor" + name + "Selective_";
}

std::string FrancaGeneratorExtensions::getStubRemoteEventClassName(
    const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "StubRemoteEvent";
}

std::string FrancaGeneratorExtensions::getStubRemoteEventClassSetMethodName(
    const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    auto name = toFirstUpper(getElementName(fAttribute));
    return "onRemoteSet" + name + "Attribute";
}

std::string FrancaGeneratorExtensions::getStubRemoteEventClassChangedMethodName(
    const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    auto name = toFirstUpper(getElementName(fAttribute));
    return "onRemote" + name + "AttributeChanged";
}

std::string FrancaGeneratorExtensions::getStubCommonAPIClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "CommonAPI::Stub<" + getStubAdapterClassName(fInterface) + ", " + getStubRemoteEventClassName(fInterface) +
           ">";
}

std::string FrancaGeneratorExtensions::generateStubReplySignature(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string signature;
    if (hasError(fMethod))
        signature = signature + getErrorNameReference(fMethod, fMethod->getContainer()) + " _error";
    if (hasError(fMethod) && !fMethod->getOutArgs().empty())
        signature = signature + ", ";

    if (!fMethod->getOutArgs().empty())
    {
        for (auto it : fMethod->getOutArgs())
        {
            if (it == fMethod->getOutArgs().front())
                signature += getTypeName(it, fMethod, true) + " _" + getElementName(it);
            else
                signature += ", " + getTypeName(it, fMethod, true) + " _" + getElementName(it);
        }
    }
    return signature;
}

std::string FrancaGeneratorExtensions::generateStubErrorReplySignature(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no error type: " + fBroadcast->getName() + "\n";
        return "";
    }

    std::string signature = "const CommonAPI::CallId_t _callId";

    if (!errorArgs(fBroadcast, deploymentAccessor).empty())
    {
        for (auto it : errorArgs(fBroadcast, deploymentAccessor))
        {
            signature = signature + ", " + getTypeName(it, fBroadcast, true) + " _" + getElementName(it);
        }
    }
    return signature;
}

std::list<std::shared_ptr<BstIdl::FArgument>> FrancaGeneratorExtensions::errorArgs(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no error type: " + fBroadcast->getName() + "\n";
        return std::list<std::shared_ptr<BstIdl::FArgument>>();
    }
    if (!fBroadcast->getOutArgs().empty())
    {
        return std::list<std::shared_ptr<BstIdl::FArgument>>();
    }
    std::list<std::shared_ptr<BstIdl::FArgument>> outArgs;
    auto it = fBroadcast->getOutArgs().begin();
    std::advance(it, 1);
    outArgs.splice(outArgs.begin(), fBroadcast->getOutArgs(), it, fBroadcast->getOutArgs().end());
    // outArgs.pop_front();
    return outArgs;
}

std::string FrancaGeneratorExtensions::getStubClassGetMethodName(const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    return "get" + toFirstUpper(fAttribute->getName()) + "Attribute";
}

std::string FrancaGeneratorExtensions::generateOverloadedStubSignature(const std::shared_ptr<BstIdl::FMethod> &fMethod,
                                                                       std::map<std::string, bool> &replies)
{
    std::string signature = "const std::shared_ptr<CommonAPI::ClientId> _client";

    if (!fMethod->isFireAndForget())
    {
        for (auto it : replies)
        {
            if (it.second == true)
            {
                // replies containing error replies
                signature = signature + ", const CommonAPI::CallId_t _callId";
            }
        }
    }

    if (!fMethod->getInArgs().empty())
        signature = signature + ", ";
    for (auto it : fMethod->getInArgs())
    {
        if (it == fMethod->getInArgs().front())
            signature = signature + getTypeName(it, fMethod, true) + " _" + getElementName(it);
        else
            signature += ", " + getTypeName(it, fMethod, true) + " _" + getElementName(it);
    }

    if (!fMethod->isFireAndForget())
    {
        for (auto entry : replies)
        {
            auto methodName = entry.first;
            auto isErrorReply = entry.second;
            signature = signature + ", " + methodName + "Reply_t";
            if (isErrorReply)
            {
                signature = signature + " _" + methodName + "Reply";
            }
            else
            {
                signature = signature + " _reply";
            }
        }
    }
    return signature;
}

std::string FrancaGeneratorExtensions::getSubscriptionRequestedMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    return "on" + toFirstUpper(fBroadcast->getName()) + "SelectiveSubscriptionRequested";
}

std::string FrancaGeneratorExtensions::stubRegisterManagedMethodWithInstanceNumberImpl(
    const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return stubRegisterManagedName(fInterface) + "(std::shared_ptr< " + getStubFullClassName(fInterface) +
           "> _stub, const uint32_t _instanceNumber)";
}

std::string FrancaGeneratorExtensions::stubRegisterManagedMethodImpl(
    const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return stubRegisterManagedName(fInterface) + "(std::shared_ptr< " + getStubFullClassName(fInterface) +
           "> _stub, const std::string &_instance)";
}

std::string FrancaGeneratorExtensions::getHeaderDefineName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    auto str = getSkeletonNamePostfix();
    transform(str.begin(), str.end(), str.begin(), ::toupper);
    return getDefineName(fInterface) + "_STUB_" + str;
}

std::string FrancaGeneratorExtensions::getStubDefaultClassName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return getElementName(fInterface) + "StubDefault" + getSkeletonNamePostfix();
}

std::string FrancaGeneratorExtensions::stubManagedSetName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "registered" + getElementName(fInterface) + "Instances";
}

std::string FrancaGeneratorExtensions::errorReplyCallbackName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no valid error type: " + fBroadcast->getName();
        return std::string();
    }
    return getElementName(fBroadcast) + "Callback";
}

std::string FrancaGeneratorExtensions::generateSendSelectiveSignatur(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast, const std::shared_ptr<BstIdl::FInterface> &fInterface,
    bool withDefault)
{
    std::string signature;
    for (auto it : fBroadcast->getOutArgs())
    {
        if (it == fBroadcast->getOutArgs().front())
            signature += "const " + getTypeName(it, fInterface, true) + " &_" + getElementName(it);
        else
        {
            signature += ", const " + getTypeName(it, fInterface, true) + " &_" + getElementName(it);
        }
    }
    if (!fBroadcast->getOutArgs().empty())
        signature = signature + ", ";

    signature = signature + "const std::shared_ptr<CommonAPI::ClientIdList> _receivers";

    if (withDefault)
        signature = signature + " = nullptr";

    return signature;
}

std::string FrancaGeneratorExtensions::getStubAdapterClassSubscribersMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    auto name = toFirstUpper(getElementName(fBroadcast));
    return "getSubscribersFor" + name + "Selective";
}

std::string FrancaGeneratorExtensions::getSubscriptionChangedMethodName(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast)
{
    return "on" + toFirstUpper(fBroadcast->getName()) + "SelectiveSubscriptionChanged";
}

std::string FrancaGeneratorExtensions::stubRegisterManagedAutoName(
    const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "registerManagedStub" + getElementName(fInterface) + "AutoInstance";
}

bool FrancaGeneratorExtensions::supportsTypeValidation(const std::shared_ptr<BstIdl::FAttribute> &fAttribute)
{
    return (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(fAttribute->getType()->getDerived()) != nullptr);
}

std::string FrancaGeneratorExtensions::validateType(const std::shared_ptr<BstIdl::FAttribute> &fAttribute,
                                                    const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(fAttribute->getType()->getDerived()) != nullptr &&
        !fAttribute->isArray())
    {
        return "_value.validate()";
    }
    else
        return "true";
}

std::list<std::shared_ptr<BstIdl::FInterface>> FrancaGeneratorExtensions::getBaseInterfaces(
    const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    std::list<std::shared_ptr<BstIdl::FInterface>> baseInterfaces;
    if (_interface->getBase() != nullptr)
    {
        bool containBase = false;
        for (auto it : getBaseInterfaces(_interface->getBase()))
        {
            baseInterfaces.emplace_back(it);
            if (_interface->getBase() == it)
            {
                containBase = true;
            }
        }
        if (!containBase)
        {
            baseInterfaces.emplace_back(_interface->getBase());
        }
    }
    return baseInterfaces;
}

std::string FrancaGeneratorExtensions::generateHashers(
    const std::shared_ptr<BstIdl::FTypeCollection> &fTypes,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    std::string str;
    for (auto type : fTypes->getTypes())
    {
        if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(type) != nullptr)
        {
            str += generateHash(type, fTypes, deploymentAccessor);
        }
    }
    return str;
}

std::string FrancaGeneratorExtensions::getClassNamespaceWithName(const std::shared_ptr<BstIdl::FModelElement> &child,
                                                                 const std::string &name,
                                                                 const std::shared_ptr<BstIdl::FModelElement> &parent,
                                                                 const std::string &parentName)
{
    auto ret = name;
    if (parent && parent != child)
    {
        ret.insert(0, parentName + "::");
    }
    return ret;
}

std::string FrancaGeneratorExtensions::getFQN(const std::shared_ptr<BstIdl::FType> &type,
                                              const std::shared_ptr<BstIdl::FTypeCollection> &fTypes)
{
    std::string str = getVersionPrefix(fTypes);
    auto list = getNamespaceAsList(std::dynamic_pointer_cast<BstIdl::FModel>(fTypes->getContainer()));
    str +=
        join(list, "::") + "::" + getClassNamespaceWithName(type, getElementName(type), fTypes, getElementName(fTypes));
    return str;
}

std::string FrancaGeneratorExtensions::generateHash(
    const std::shared_ptr<BstIdl::FType> &type, const std::shared_ptr<BstIdl::FTypeCollection> &fTypes,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    auto name = toFirstLower(getElementName(type));

    auto str = "//Hash for " + getElementName(type) + "\ntemplate<>";
    str += "\nstruct hash<" + getFQN(type, fTypes) + "> {";
    str += "\n\tinline size_t operator()(const " + getFQN(type, fTypes) + "& " + name + ") const {";
    str += "\n\t\treturn static_cast< " +
           getPrimitiveTypeName(
               getBackingType(std::dynamic_pointer_cast<BstIdl::FEnumerationType>(type), deploymentAccessor)) +
           ">(" + name + ");\n\t}\n};\n";
    return str;
}

std::string FrancaGeneratorExtensions::generateIndent(int _indent)
{
    std::string empty = "";
    int i = 0;
    while (i < _indent)
    {
        empty = empty + "    ";
        i = i + 1;
    }
    return empty;
}

bool FrancaGeneratorExtensions::isStructEmpty(const std::shared_ptr<BstIdl::FStructType> &fStructType)
{
    if (!fStructType->getElements().empty() || fStructType->isPolymorphic())
    {
        return false;
    }
    if (fStructType->getBase() != nullptr)
    {
        return isStructEmpty(fStructType->getBase());
    }
    return true;
}

std::string FrancaGeneratorExtensions::getRelativeName(const std::shared_ptr<BstIdl::FModelElement> &_element)
{
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(_element) != nullptr)
        return "";
    std::string next = getRelativeName(std::dynamic_pointer_cast<BstIdl::FModelElement>(_element->getContainer()));
    if (next != "")
        return next + "_" + _element->getName();
    return _element->getName();
}

std::string FrancaGeneratorExtensions::getBitWidth(const std::shared_ptr<BstIdl::FBasicTypeId> &_typeId)
{
    if (_typeId->getValue() == BstIdl::FBasicTypeId::BOOLEAN || _typeId->getValue() == BstIdl::FBasicTypeId::INT8 ||
        _typeId->getValue() == BstIdl::FBasicTypeId::UINT8)
        return "8";

    if (_typeId->getValue() == BstIdl::FBasicTypeId::INT16 || _typeId->getValue() == BstIdl::FBasicTypeId::UINT16)
        return "16";

    if (_typeId->getValue() == BstIdl::FBasicTypeId::INT32 || _typeId->getValue() == BstIdl::FBasicTypeId::UINT32)
        return "32";

    if (_typeId->getValue() == BstIdl::FBasicTypeId::INT64 || _typeId->getValue() == BstIdl::FBasicTypeId::UINT64)
        return "64";

    return "";
}

std::string FrancaGeneratorExtensions::isSigned(std::string _typeName)
{
    if (_typeName == "Int8" || _typeName == "Int16" || _typeName == "Int32" || _typeName == "Int64")
    {
        return "true";
    }
    return "false";
}

std::string FrancaGeneratorExtensions::startInternalCompilation()
{
    std::string str = R"(
#if !defined (COMMONAPI_INTERNAL_COMPILATION)
#define COMMONAPI_INTERNAL_COMPILATION
#define HAS_DEFINED_COMMONAPI_INTERNAL_COMPILATION_HERE
#endif
)";
    return str;
}

std::string FrancaGeneratorExtensions::endInternalCompilation()
{
    std::string str = R"(
#if defined (HAS_DEFINED_COMMONAPI_INTERNAL_COMPILATION_HERE)
#undef COMMONAPI_INTERNAL_COMPILATION
#undef HAS_DEFINED_COMMONAPI_INTERNAL_COMPILATION_HERE
#endif
)";
    return str;
}

std::string FrancaGeneratorExtensions::generateSomeIPBaseInstantiations(
    const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    auto baseInterfaces = getBaseInterfaces(_interface);
    std::string str;
    for (auto it : baseInterfaces)
    {
        if (it == baseInterfaces.front())
            str += getTypeCollectionName(it, _interface) + "SomeIPProxy(_address, _connection)";
        else
            str += ",\n" + getTypeCollectionName(it, _interface) + "SomeIPProxy(_address, _connection)";
    }
    return str;
}

std::string FrancaGeneratorExtensions::getErrorType(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    std::string errorType = "";
    if (hasError(fMethod))
    {
        errorType = getErrorNameReference(fMethod, fMethod->getContainer());
    }
    return errorType;
}

std::string FrancaGeneratorExtensions::generateBaseRemoteHandlerConstructorsCalls(
    const std::shared_ptr<BstIdl::FInterface> &_interface)
{
    auto baseInterfaces = getBaseInterfaces(_interface);
    std::string itsCalls = "";
    for (auto it : baseInterfaces)
    {
        itsCalls += getTypeCollectionName(it, _interface) + "StubDefault::RemoteEventHandler(_defaultStub), ";
    }
    return itsCalls;
}

std::list<std::shared_ptr<BstIdl::FModelElement>> FrancaGeneratorExtensions::getElements(
    const std::shared_ptr<BstIdl::FInterface> &_iface)
{
    std::list<std::shared_ptr<BstIdl::FModelElement>> itsElements;

    // Add attributes, method, broadcasts and (for completeness only) constants
    for (auto attribute : _iface->getAttributes())
    {
        itsElements.push_back(attribute);
    }

    for (auto broadcast : _iface->getBroadcasts())
    {
        itsElements.push_back(broadcast);
    }

    for (auto constant : _iface->getConstants())
    {
        itsElements.push_back(constant);
    }

    for (auto method : _iface->getMethods())
    {
        itsElements.push_back(method);
    }

    // Return the values from the sorted(!) map
    return itsElements;
}

int FrancaGeneratorExtensions::getElementPosition(const std::shared_ptr<BstIdl::FInterface> &_iface,
                                                  const std::shared_ptr<BstIdl::FModelElement> &_elem)
{
    auto i = 0;
    for (auto itsElement : getElements(_iface))
    {
        if (itsElement == _elem)
            return i;
        i = i + 1;
    }
    return i;
}

std::string FrancaGeneratorExtensions::errorReplyCallbackBindArgs(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no valid error type: " + fBroadcast->getName() << std::endl;
        return std::string();
    }
    std::string bindArgs = "std::placeholders::_1, \"" + deploymentAccessor->getErrorName(fBroadcast) + "\"";
    if (errorArgs(fBroadcast, deploymentAccessor).size() > 1)
    {
        for (auto i = 0; i < errorArgs(fBroadcast, deploymentAccessor).size(); i++)
        {
            bindArgs = bindArgs + ", std::placeholders::_" + std::to_string(i + 2);
        }
    }
    return bindArgs;
}

std::string FrancaGeneratorExtensions::errorName(const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast,
                                                 const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no valid error type: " + fBroadcast->getName() << std::endl;
        return std::string();
    }
    if (fBroadcast->getOutArgs().empty())
    {
        return std::string();
    }
    if (getTypeName(fBroadcast->getOutArgs().front(), fBroadcast, true) == "std::string")
    {
        std::cout << "FBroadcast of type error does not contain a string as first argument (error name): " +
                         fBroadcast->getName()
                  << std::endl;
        return std::string();
    }

    return getElementName(fBroadcast->getOutArgs().front());
}

std::string FrancaGeneratorExtensions::generateErrorReplyCallbackSignature(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast, const std::shared_ptr<BstIdl::FMethod> &fMethod,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no valid error type: " + fBroadcast->getName();
        return std::string();
    }
    std::string signature = "const CommonAPI::CallId_t _call";
    std::string signatureOut;
    for (auto it : fBroadcast->getOutArgs())
    {
        if (it == fBroadcast->getOutArgs().front())
        {
            signatureOut += getTypeName(it, fBroadcast, true) + " _" + getElementName(it);
        }
        else
            signatureOut += ", " + getTypeName(it, fBroadcast, true) + " _" + getElementName(it);
    }
    if (!signatureOut.empty())
    {
        signature = signature + ", ";
    }
    signature = signature + signatureOut;
    return signature;
}

std::string FrancaGeneratorExtensions::errorReplyTypes(
    const std::shared_ptr<BstIdl::FBroadcast> &fBroadcast, const std::shared_ptr<BstIdl::FMethod> &fMethod,
    const std::shared_ptr<CommonapiPropertyAccessor> &deploymentAccessor)
{
    if (!isErrorType(fBroadcast, deploymentAccessor))
    {
        std::cout << "FBroadcast is no valid error type: " + fBroadcast->getName();
        return std::string();
    }
    std::string types = "CommonAPI::CallId_t";
    std::string errorTypes;
    for (auto it : errorArgs(fBroadcast, deploymentAccessor))
    {
        if (it == errorArgs(fBroadcast, deploymentAccessor).front())
        {
            errorTypes += getTypeName(it, fBroadcast, true);
        }
        else
        {
            errorTypes += ", " + getTypeName(it, fBroadcast, true);
        }
    }
    if (!errorTypes.empty())
    {
        types += ", ";
    }
    types = types + errorTypes;
    return types;
}

std::string FrancaGeneratorExtensions::doCast(std::string _value, std::string _backingType)
{
    if (isSignedBackingType(_backingType))
    {
        int64_t itsValue = atoll(_value.c_str());
        int64_t itsSignedMax;
        int64_t itsUnsignedMax;
        if (_backingType == "int8_t")
        {
            itsSignedMax = 127;
            itsUnsignedMax = 255;
        }
        else if (_backingType == "int16_t")
        {
            itsSignedMax = 32767;
            itsUnsignedMax = 65535;
        }
        else if (_backingType == "int32_t")
        {
            itsSignedMax = 2147483647;
            itsUnsignedMax = 4294967295;
        }
        else
        {
            itsSignedMax = 9223372036854775807UL;
            itsUnsignedMax = 18446744073709551615UL;
        }
        if (itsValue > itsSignedMax && itsValue <= itsUnsignedMax)
        {
            itsValue = itsValue - itsUnsignedMax - 1;
            return std::to_string(itsValue);
        }
    }
    return _value;
}

std::list<std::string> FrancaGeneratorExtensions::getSubnamespaceList(
    const std::shared_ptr<BstIdl::FModelElement> &destination, const std::shared_ptr<BstIdl::FObject> &source)
{
    auto model = std::dynamic_pointer_cast<BstIdl::FModel>(source);
    auto modelElement = std::dynamic_pointer_cast<BstIdl::FModelElement>(source);
    std::list<std::string> sourceNamespaceList;
    if (model != nullptr)
        sourceNamespaceList = getNamespaceAsList(model);
    else if (modelElement != nullptr)
        sourceNamespaceList = getNamespaceAsList(modelElement);
    auto destinationNamespaceList = getNamespaceAsList(destination);
    size_t maxCount;
    if (sourceNamespaceList.size() < destinationNamespaceList.size())
        maxCount = sourceNamespaceList.size();
    else
    {
        maxCount = destinationNamespaceList.size();
    }
    auto dropCount = 0;
    while (dropCount < maxCount && (sourceNamespaceList.front() == destinationNamespaceList.front()))
    {
        sourceNamespaceList.pop_front();
        destinationNamespaceList.pop_front();
        dropCount += 1;
    }
    return destinationNamespaceList;
}

std::string FrancaGeneratorExtensions::getBasicAsyncCallbackClassName(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    auto name = getElementName(fMethod);
    transform(name.begin(), name.begin() + 1, name.begin(), ::toupper);
    return name + "AsyncCallback";
}

std::string FrancaGeneratorExtensions::getMangledAsyncCallbackClassName(const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    auto baseName = getBasicAsyncCallbackClassName(fMethod) + "_";
    std::string mangledName = "";
    for (auto it : fMethod->getOutArgs())
    {
        mangledName = mangledName + getMangledName(it->getType());
    }
    if (fMethod->getErrorEnum() != nullptr)
    {
        mangledName += fMethod->getErrorEnum()->getName();
    }
    return baseName + mangledName;
}

std::string FrancaGeneratorExtensions::getBasicMangledName(const std::shared_ptr<BstIdl::FBasicTypeId> &basicType)
{
    switch (basicType->getValue())
    {
    case BstIdl::FBasicTypeId::BOOLEAN:
        return "b";
    case BstIdl::FBasicTypeId::INT8:
        return "i8";
    case BstIdl::FBasicTypeId::UINT8:
        return "u8";
    case BstIdl::FBasicTypeId::INT16:
        return "i16";
    case BstIdl::FBasicTypeId::UINT16:
        return "u16";
    case BstIdl::FBasicTypeId::INT32:
        return "i32";
    case BstIdl::FBasicTypeId::UINT32:
        return "u32";
    case BstIdl::FBasicTypeId::INT64:
        return "i64";
    case BstIdl::FBasicTypeId::UINT64:
        return "u64";
    case BstIdl::FBasicTypeId::FLOAT:
        return "f";
    case BstIdl::FBasicTypeId::DOUBLE:
        return "d";
    case BstIdl::FBasicTypeId::STRING:
        return "s";
    case BstIdl::FBasicTypeId::BYTE_BUFFER:
        return "au8";
    default: {
        return nullptr;
    }
    }
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FEnumerationType> &fType)
{
    return "Ce" + getFullyQualifiedName(fType);
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FMapType> &fType)
{
    return "Cm" + getFullyQualifiedName(fType);
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FStructType> &fType)
{
    return "Cs" + getFullyQualifiedName(fType);
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FUnionType> &fType)
{
    return "Cv" + getFullyQualifiedName(fType);
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FArrayType> &fType)
{
    return "Ca" + getFullyQualifiedName(fType);
}

std::string FrancaGeneratorExtensions::getDerivedMangledName(const std::shared_ptr<BstIdl::FTypeDef> &fType)
{
    return getMangledName(fType->getActualType());
}

std::string FrancaGeneratorExtensions::getMangledName(const std::shared_ptr<BstIdl::FTypeRef> &fTypeRef)
{
    if (fTypeRef->getDerived() != nullptr)
    {
        auto derived = fTypeRef->getDerived();
        if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FEnumerationType>(derived));
        if (std::dynamic_pointer_cast<BstIdl::FMapType>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FMapType>(derived));
        if (std::dynamic_pointer_cast<BstIdl::FStructType>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FStructType>(derived));
        if (std::dynamic_pointer_cast<BstIdl::FUnionType>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FUnionType>(derived));
        if (std::dynamic_pointer_cast<BstIdl::FArrayType>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FArrayType>(derived));
        if (std::dynamic_pointer_cast<BstIdl::FTypeDef>(derived) != nullptr)
            return getDerivedMangledName(std::dynamic_pointer_cast<BstIdl::FTypeDef>(derived));

        return std::string();
    }
    else
    {
        return getBasicMangledName(fTypeRef->getPredefined());
    }
}

std::string FrancaGeneratorExtensions::generateDummyArgumentInitialization(
    const std::shared_ptr<BstIdl::FTypeRef> &typeRef, const std::shared_ptr<BstIdl::FArgument> &list_element,
    const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    if (list_element->isArray())
    {
        return " = {}";
    }
    else if (typeRef->getDerived())
    {
        return generateDummyArgumentInitialization(typeRef->getDerived(), list_element, fMethod);
    }
    else if (typeRef->getPredefined())
    {
        return generateDummyArgumentInitialization(typeRef->getPredefined());
    }
    else
    {
        return "";
    }
}

std::string FrancaGeneratorExtensions::generateDummyArgumentInitialization(
    const std::shared_ptr<BstIdl::FType> &type, const std::shared_ptr<BstIdl::FArgument> &list_element,
    const std::shared_ptr<BstIdl::FMethod> &fMethod)
{
    if (std::dynamic_pointer_cast<BstIdl::FArrayType>(type) || std::dynamic_pointer_cast<BstIdl::FCompoundType>(type))
    {
        return " = {}";
    }
    else if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(type))
    {
        if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(type)->getEnumerators().empty())
        {
            return " = " + getTypeName(list_element, fMethod, true) + "(0u)";
        }
        else
        {
            return " = " + getTypeName(list_element, fMethod, true) + "::" + getEnumPrefix() +
                   getElementName(std::dynamic_pointer_cast<BstIdl::FEnumerationType>(type)->getEnumerators().front());
        }
    }
    else if (std::dynamic_pointer_cast<BstIdl::FIntegerInterval>(type))
    {
        return " = " + std::to_string(std::dynamic_pointer_cast<BstIdl::FIntegerInterval>(type)->getLowerBound());
    }
    else if (std::dynamic_pointer_cast<BstIdl::FTypeDef>(type))
    {
        return generateDummyArgumentInitialization(std::dynamic_pointer_cast<BstIdl::FTypeDef>(type)->getActualType(),
                                                   list_element, fMethod);
    }
    else
    {
        return "";
    }
}

std::string FrancaGeneratorExtensions::generateDummyArgumentInitialization(
    const std::shared_ptr<BstIdl::FBasicTypeId> &basicType)
{
    switch (basicType->getValue())
    {
    case BstIdl::FBasicTypeId::BOOLEAN:
        return " = false";
    case BstIdl::FBasicTypeId::INT8:
        return " = 0";
    case BstIdl::FBasicTypeId::UINT8:
        return " = 0u";
    case BstIdl::FBasicTypeId::INT16:
        return " = 0";
    case BstIdl::FBasicTypeId::UINT16:
        return " = 0u";
    case BstIdl::FBasicTypeId::INT32:
        return " = 0";
    case BstIdl::FBasicTypeId::UINT32:
        return " = 0ul";
    case BstIdl::FBasicTypeId::INT64:
        return " = 0";
    case BstIdl::FBasicTypeId::UINT64:
        return " = 0ull";
    case BstIdl::FBasicTypeId::FLOAT:
        return " = 0.0f";
    case BstIdl::FBasicTypeId::DOUBLE:
        return " = 0.0";
    case BstIdl::FBasicTypeId::STRING:
        return " = \"\"";
    case BstIdl::FBasicTypeId::BYTE_BUFFER:
        return " = {}";
    default:
        return "";
    }
}

std::string FrancaGeneratorExtensions::generateDeploymentNamespaceBegin(
    const std::shared_ptr<BstIdl::FTypeCollection> &_tc)
{
    return "namespace " + getElementName(_tc) + "_ {";
}

std::shared_ptr<BstIdl::FTypeCollection> FrancaGeneratorExtensions::getContainingTypeCollection(
    const std::shared_ptr<BstIdl::FModelElement> &fModelElement)
{
    if (fModelElement->getContainer() == nullptr ||
        std::dynamic_pointer_cast<BstIdl::FModel>(fModelElement->getContainer()))
    {
        return nullptr;
    }
    if (std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement->getContainer()))
    {
        return std::dynamic_pointer_cast<BstIdl::FTypeCollection>(fModelElement->getContainer());
    }

    return getContainingTypeCollection(std::dynamic_pointer_cast<BstIdl::FModelElement>(fModelElement->getContainer()));
}

std::string FrancaGeneratorExtensions::proxyManagerMemberName(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    return "proxyManager" + getElementName(fInterface) + "_";
}

std::size_t FrancaGeneratorExtensions::getRsize(const std::shared_ptr<BstIdl::FInterface> &fInterface)
{
    if (!fInterface)
        return 0;

    auto ret =
        fInterface->getAttributes().size() + fInterface->getMethods().size() + fInterface->getBroadcasts().size();
    if (auto ptr = fInterface->getBase())
        ret += getRsize(ptr);

    return ret;
}

void FrancaGeneratorExtensions::addFTypeDirectlyReferencedTypes(std::list<std::shared_ptr<BstIdl::FType>> &list,
                                                                std::shared_ptr<BstIdl::FType> fType)
{
    if (std::dynamic_pointer_cast<BstIdl::FStructType>(fType) != nullptr)
    {
        auto elements = std::dynamic_pointer_cast<BstIdl::FStructType>(fType)->getElements();
        for (auto element : elements)
        {
            auto derived = element->getType()->getDerived();
            if (derived != nullptr)
            {
                list.push_back(derived);
            }
        }
        return;
    }
    if (std::dynamic_pointer_cast<BstIdl::FEnumerationType>(fType) != nullptr)
    {
        auto base = std::dynamic_pointer_cast<BstIdl::FEnumerationType>(fType)->getBase();
        if (base != nullptr)
        {
            list.push_back(base);
        }
        return;
    }
    if (std::dynamic_pointer_cast<BstIdl::FArrayType>(fType) != nullptr)
    {
        auto derived = std::dynamic_pointer_cast<BstIdl::FArrayType>(fType)->getElementType()->getDerived();
        if (derived != nullptr)
        {
            list.push_back(derived);
        }
        return;
    }
    if (std::dynamic_pointer_cast<BstIdl::FUnionType>(fType) != nullptr)
    {
        auto elements = std::dynamic_pointer_cast<BstIdl::FUnionType>(fType)->getElements();
        for (auto element : elements)
        {
            auto derived = element->getType()->getDerived();
            if (derived != nullptr)
            {
                list.push_back(derived);
            }
        }
        return;
    }
    if (std::dynamic_pointer_cast<BstIdl::FMapType>(fType) != nullptr)
    {
        auto keyDerived = std::dynamic_pointer_cast<BstIdl::FMapType>(fType)->getKeyType()->getDerived();
        if (keyDerived != nullptr)
        {
            list.push_back(keyDerived);
        }
        auto valueDerived = std::dynamic_pointer_cast<BstIdl::FMapType>(fType)->getValueType()->getDerived();
        if (valueDerived != nullptr)
        {
            list.push_back(valueDerived);
        }
        return;
    }
    if (std::dynamic_pointer_cast<BstIdl::FTypeDef>(fType) != nullptr)
    {
        auto derived = std::dynamic_pointer_cast<BstIdl::FTypeDef>(fType)->getActualType()->getDerived();
        if (derived != nullptr)
        {
            list.push_back(derived);
        }
        return;
    }
}

std::list<std::shared_ptr<BstIdl::FType>> FrancaGeneratorExtensions::getDirectlyReferencedTypes(
    const std::shared_ptr<BstIdl::FType> &type)
{
    directlyReferencedTypes.clear();
    addFTypeDirectlyReferencedTypes(directlyReferencedTypes, type);
    return directlyReferencedTypes;
}

void FrancaGeneratorExtensions::mergeDeployments(const std::shared_ptr<BstIdl::FDInterface> &_source,
                                                 const std::shared_ptr<BstIdl::FDInterface> &_target)
{
    if (!_source || !_target || !_source->getTarget() || !_target->getTarget() ||
        _source->getTarget() != _target->getTarget())
        return;

    // Merge attributes
    for (auto s : _source->getAttributes())
    {
        bool hasBeenMerged = false;
        for (auto t : _target->getAttributes())
        {
            if (s->getTarget() == t->getTarget())
            {
                for (auto p : s->getProperties())
                    addListItem(t->getProperties(), p);
                hasBeenMerged = true;
            }
        }
        if (!hasBeenMerged)
            addListItem(_target->getAttributes(), s);
    }

    // Merge broadcasts
    for (auto s : _source->getBroadcasts())
    {
        bool hasBeenMerged = false;
        for (auto t : _target->getBroadcasts())
        {
            if (s->getTarget() == t->getTarget())
            {
                for (auto p : s->getProperties())
                    addListItem(t->getProperties(), p);
                hasBeenMerged = true;
            }
        }
        if (!hasBeenMerged)
        {
            addListItem(_target->getBroadcasts(), s);
        }
    }

    // Merge methods
    for (auto s : _source->getMethods())
    {
        bool hasBeenMerged = false;
        for (auto t : _target->getMethods())
        {
            if (s->getTarget() == t->getTarget())
            {
                for (auto p : s->getProperties())
                    addListItem(t->getProperties(), p);
                hasBeenMerged = true;
            }
        }
        if (!hasBeenMerged)
            addListItem(_target->getMethods(), s);
    }

    // Merge types
    mergeDeployments(_source->getTypes(), _target->getTypes());
}

void FrancaGeneratorExtensions::mergeDeployments(const std::shared_ptr<BstIdl::FDTypes> &_source,
                                                 const std::shared_ptr<BstIdl::FDInterface> &_target)
{
    mergeDeployments(_source->getTypes(), _target->getTypes());
}

void FrancaGeneratorExtensions::mergeDeployments(const std::shared_ptr<BstIdl::FDTypes> &_source,
                                                 const std::shared_ptr<BstIdl::FDTypes> &_target)
{
    if (!_source || !_target || !_source->getTarget() || !_target->getTarget() ||
        _source->getTarget() != _target->getTarget())
        return;
    mergeDeployments(_source->getTypes(), _target->getTypes());
}

void FrancaGeneratorExtensions::mergeDeploymentsExt(const std::shared_ptr<BstIdl::FDTypes> &_source,
                                                    const std::shared_ptr<BstIdl::FDTypes> &_target)
{
    mergeDeployments(_source, _target);
}

void FrancaGeneratorExtensions::mergeDeployments(const std::list<std::shared_ptr<BstIdl::FDTypeDefinition>> &_source,
                                                 std::list<std::shared_ptr<BstIdl::FDTypeDefinition>> &_target)
{
    for (auto s : _source)
    {
        bool hasBeenMerged = false;
        for (auto t : _target)
        {
            if (std::dynamic_pointer_cast<BstIdl::FDArray>(s) && std::dynamic_pointer_cast<BstIdl::FDArray>(t))
            {
                auto a = std::dynamic_pointer_cast<BstIdl::FDArray>(s);
                auto b = std::dynamic_pointer_cast<BstIdl::FDArray>(t);
                if (a->getTarget() == b->getTarget())
                {
                    for (auto p : a->getProperties())
                        addListItem(b->getProperties(), p);
                    hasBeenMerged = true;
                }
            }
            else if (std::dynamic_pointer_cast<BstIdl::FDEnumeration>(s) &&
                     std::dynamic_pointer_cast<BstIdl::FDEnumeration>(t))
            {
                auto a = std::dynamic_pointer_cast<BstIdl::FDEnumeration>(s);
                auto b = std::dynamic_pointer_cast<BstIdl::FDEnumeration>(t);
                if (a->getTarget() == b->getTarget())
                {
                    for (auto p : a->getProperties())
                        addListItem(b->getProperties(), p);
                    hasBeenMerged = true;
                }
            }
            else if (std::dynamic_pointer_cast<BstIdl::FDStruct>(s) && std::dynamic_pointer_cast<BstIdl::FDStruct>(t))
            {
                auto a = std::dynamic_pointer_cast<BstIdl::FDStruct>(s);
                auto b = std::dynamic_pointer_cast<BstIdl::FDStruct>(t);
                if (a->getTarget() == b->getTarget())
                {
                    for (auto p : a->getProperties())
                        addListItem(b->getProperties(), p);
                    hasBeenMerged = true;
                }
            }
            else if (std::dynamic_pointer_cast<BstIdl::FDUnion>(s) && std::dynamic_pointer_cast<BstIdl::FDUnion>(t))
            {
                auto a = std::dynamic_pointer_cast<BstIdl::FDUnion>(s);
                auto b = std::dynamic_pointer_cast<BstIdl::FDUnion>(t);
                if (a->getTarget() == b->getTarget())
                {
                    for (auto p : a->getProperties())
                        addListItem(b->getProperties(), p);
                    hasBeenMerged = true;
                }
            }
        }
        if (!hasBeenMerged)
            addListItem(_target, s);
    }
}
} // namespace BstCommonAPI